嗨大家好,我是Eric,在上集中,我們講了瀏覽器有多個進程和線程,以及渲染主線程(Main Thread in Renderer Process)有多麼的忙碌。
今天我們來深入探討事件迴圈(Event Loop)是如何幫助主線程有效地處理這一堆任務。
事件迴圈就像廚師的助手,幫忙看著「訂單(任務)」是不是已經做好了。簡單來說,它會一直檢查有沒有新任務要處理,有就拿來做,沒有就稍等一會。
// 簡單範例
while(true) {
let task = messageQueue.shift();
if(task) {
execute(task);
}
}
消息隊列就像廚房裡的訂單,先來的先做(FIFO,First-In-First-Out),但是瀏覽器有多個不同類型的隊列,像是微任務隊列(Microtask Queue)和宏任務隊列(Macrotask Queue),而有些訂單(微任務)比較急,所以會優先處理。。
延遲隊列:用於儲存計時器到達後的回調任務,優先級「中」(Delay Queue)
交互隊列:用於儲存用戶操作後產生的事件處理任務,優先級「高」(Interaction Queue)
微隊列:用於儲存需要最快執行的任務,優先級「最高」(Microtask Queue)
// Promise 是個急迫的客人(微任務)
Promise.resolve().then(() => {
console.log("這個訂單很急!");
});
// setTimeout 是比較悠閒的客人(延遲隊列)
setTimeout(() => {
console.log("這個訂單可以等等!");
}, 0);
可能有人會問,為什麼不用多個線程來同時執行這些任務呢?其實,JavaScript 是單線程的,這意味著一次只能執行一個任務。如果兩個線程同時改變 DOM 或其他共享資源,那可能會引發一些很嚴重的問題(比如資料不一致)。
在上面的介紹中,我們講到了許多關於事件迴圈(Event Loop)的核心概念。但是,還有一個非常重要的主題需要討論,那就是「異步(Asynchronous)」。
在JavaScript世界中,異步不等於多線程。JavaScript 是單線程的,這意味著一次只能做一件事。但是,異步操作允許我們避免阻塞這個單一的線程。
比如,當你使用 fetch() 函數去獲取一些遠程資料時,JavaScript 會將這個任務交給瀏覽器的其他部分(可能是Web API或是瀏覽器的其他線程)來處理,然後它會繼續執行下面的程式碼,不會等待 fetch() 的完成。
如果使用同步的方式,就極有可能導致主線程產生阻塞,從而導致消息隊列中的很多其他任務無法得到執行。這樣一來,一方面會導致繁忙的主線程白白地消耗時間,另一方面導致頁面無法及時更新,給 User 造成卡死現象。
// 異步操作示例
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
console.log('我不會等 fetch 完成');
了解事件迴圈(Event Loop)對於優化你的網站和提供更好的用戶體驗是非常重要的。只有明白了這些底層的運作原理,我們才能寫出更高效、更流暢的網頁。
希望這個介紹能幫助大家更好地理解事件迴圈(Event Loop)和它在前端開發中的重要性!謝謝大家~~~明天見!
參考資料:W3C HTML Spec - Event Loop